<?php
/**
 * KLF Framework
 *  
 * @author Kristian Oye <kristianoye@gmail.com>
 */
namespace KLF\System\Collections
{
	use KLF\System\KType;

	class KListIterator implements \Iterator
	{
		private $_current=0;
		private $_source;

		public function __construct($source)
		{
			$this->_source =& $source;
		}
		
		public function current()
		{
			return $this->_source[$this->_current];
		}
		
		public function key()
		{
			return $this->_current;
		}
		
		public function next()
		{
			return $this->_current++;
		}
		
		public function rewind()
		{
			$this->_current = 0;
		}
		
		public function valid()
		{
			return ($this->_current < $this->_source->count());
		}
	}

	/**
	 *  Class KList
	 *  
	 *  Implements an ordered-list of items.
	 *
	 *  @package KLF\System\Collections
	 *  @access public
	 *  @author Kristian Oye <kristianoye@gmail.com>
	 *  @since 1.0
	 *  
	 *  @method KList NewInstance
	 */
	class KList extends KCollection
	{
		/**
		 * @var array
		 */
		protected $_Data = array();
		
		/**
		 * The type contained within the list (null if not enforced)
		 * @var mixed
		 */
		protected $_rpropDataType = null;
		
		/**
		 * The number of elements in the collection.
		 * @var int
		 */
		protected $_Size = 0;
		
		/**
		 * Constructs a new KList object.  Optionally can 
		 * specify the desired starting size.
		 *
		 * @author Kristian Oye <kristianoye@gmail.com>
		 * @since 0.1
		 * @return KList Reference to the KList object.
		*/
		public function __construct($strongType=null, $initialSize = 0)
		{
			if (!is_null($strongType))
			{
				if (KType::GetType($strongType) === KType::GetType())
				{
					$this->_rpropDataType = $strongType;
				}
				else
				{
					$this->_rpropDataType = KType::GetTypeByClass($this->_rpropDataType);
				}
			}
			
			$this->_Data = ($initialSize > 0 ? 
				array_pad(array(), $initialSize, 0) : array());
			$this->_Size = $initialSize;
		}
		
		/**
		 * Adds an object to the KList collection and returns a reference
		 * to the list container.
		 *
		 * @author Kristian Oye <kristianoye@gmail.com>
		 * @since 1.0
		 * @return KList Reference to the KList object.
		*/
		public function Add($element)
		{
			KCollection::__validateValue($element, 'value', $this->_rpropDataType);
			$this->_Data[] = $element;
			$this->_Size ++;
			
			return $this;
		}

		/**
		 * Clears all elements from the list
		 *
		 * @author Kristian Oye <kristianoye@gmail.com>
		 * @since 1.0
		 * @return KList Reference to the KList object.
		*/
		public function Clear()
		{
			$this->_Data = array();
			$this->_Size = 0;
			
			return $this;
		}
		
		/**
		 * Returns the number of items in the list.
		 *
		 * @author Kristian Oye <kristianoye@gmail.com>
		 * @since 1.0
		 * @return int The number of items in the list or 0 if empty.
		*/
		public function count()
		{
			return $this->_Size;
		}
		
		/**
		 * Returns an object used to iterate through the collection.
		 *
		 * @author Kristian Oye <kristianoye@gmail.com>
		 * @since 1.0
		 * @return KListIterator The iterator for looping.
		*/
		public function getIterator()
		{
			return new KListIterator($this);
		}
		
		public function Get($index)
		{
			return $this->offsetGet($index);
		}
		
		/**
		 * Checks to see if a particular index exists in the list.
		 *
		 * @author Kristian Oye <kristianoye@gmail.com>
		 * @since 1.0
		 * @return boolean true if the index exists, false if not.
		*/
		public function offsetExists($offset)
		{
			KCollection::__validateValue($offset, 'index', KType::GetTypeByClass('integer'));
			return isset($this->_Data[$offset]); 
		}
		
		/**
		 * Returns the value associated with a particular index.
		 *
		 * @author Kristian Oye <kristianoye@gmail.com>
		 * @since 1.0
		 * @return mixed The value at the given offset.
		*/
		public function offsetGet($offset)
		{
			KCollection::__validateValue($offset, 'index', KType::GetTypeByClass('integer'));
			
			return (isset($this->_Data[$offset]) ? 
				$this->_Data[$offset] : null);
		}
		
		/**
		 * Sets the value at a particular offset in the list.
		 *
		 * @author Kristian Oye <kristianoye@gmail.com>
		 * @since 1.0
		 * @return mixed Returns the newly set value.
		*/
		public function offsetSet($offset, $value)
		{
			KCollection::__validateValue($offset, 'index', KType::GetTypeByClass('integer'));
			KCollection::__validateValue($value, 'value', $this->_DataType);
			
			if (is_null($offset))
			{
				return ($this->_Data[] = $value);
			}
			else
			{
				return ($this->_Data[$offset] = $value);
			}
		}
		
		/**
		 * Unsets a value at the specified offset.
		 *
		 * @author Kristian Oye <kristianoye@gmail.com>
		 * @since 1.0
		*/
		public function offsetUnset($offset)
		{
			KCollection::__validateValue($offset, 'index', KType::GetTypeByClass('integer'));
			unset($this->_Data[$offset]);
		}
	}
}
